package com.soup.common.support.spring.advice;

import com.soup.common.entity.ErrorCode;
import com.soup.common.entity.Result;
import com.soup.common.exception.ServiceException;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.converter.HttpMessageNotReadableException;
import org.springframework.validation.BindException;
import org.springframework.validation.BindingResult;
import org.springframework.validation.FieldError;
import org.springframework.web.HttpRequestMethodNotSupportedException;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.MissingServletRequestParameterException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import org.springframework.web.multipart.MaxUploadSizeExceededException;
import org.springframework.web.multipart.support.MissingServletRequestPartException;
import org.springframework.web.servlet.NoHandlerFoundException;

import javax.validation.ConstraintViolation;
import javax.validation.ConstraintViolationException;
import javax.validation.ValidationException;
import java.util.Set;

/**
 * 全局异常接管
 * 注意：
 *      启用全局异常接管后，没有在此处定义拦截的异常都会默认返回500错误。
 *      若需要自定义拦截的异常，请在此处定义拦截。
 *      若需要输出异常的日志日志，请使用log输出。
 *
 * @author zhaoyi
 */
@Slf4j
@RestControllerAdvice
public class ExceptionsHandlerAdvice {

    /**
     * 基本异常
     */
    @ExceptionHandler(Exception.class)
    public Result exception(Exception e) {
        log.error("错误信息: {}, 异常类: {}", e.getMessage(), e.getClass(), e);
        return new Result().fail(ErrorCode.SERVER_FAIL);
    }

    /**
     * 校验异常
     *
     * @param exception 异常
     * @return 结果
     */
    @ExceptionHandler(ValidationException.class)
    public Result handle(ValidationException exception) {
        log.error("错误信息: {}, 异常类: {}", exception.getMessage(), exception.getClass());
        String errorMessage = "";
        if (exception instanceof ConstraintViolationException) {
            StringBuilder errorInfo = new StringBuilder();
            ConstraintViolationException exs = (ConstraintViolationException) exception;
            Set<ConstraintViolation<?>> violations = exs.getConstraintViolations();
            for (ConstraintViolation<?> item : violations) {
                errorInfo.append(item.getMessage()).append(",");
            }
            errorMessage = errorInfo.toString().substring(0, errorInfo.toString().length() - 1);
        }
        return new Result().fail(ErrorCode.PARAM_ERROR, errorMessage);
    }

    /**
     * 校验异常
     *
     * @param e 异常
     * @return 结果
     */
    @ExceptionHandler(MethodArgumentNotValidException.class)
    public Result argumentNotValid(MethodArgumentNotValidException e) {
        log.error("错误信息: {}, 异常类: {}", e.getMessage(), e.getClass());
        StringBuilder errorInfo = new StringBuilder();
        BindingResult bindingResult = e.getBindingResult();
        for(int i = 0; i < bindingResult.getFieldErrors().size(); i++){
            if(i > 0){
                errorInfo.append(",");
            }
            FieldError fieldError = bindingResult.getFieldErrors().get(i);
            errorInfo.append(fieldError.getField()).append(" :").append(fieldError.getDefaultMessage());
        }
        return new Result().fail(ErrorCode.PARAM_ERROR, errorInfo.toString());
    }

    /**
     * 请求路径无法找到异常
     */
    @ExceptionHandler(NoHandlerFoundException.class)
    public Result notFoundException(Exception e) {
        log.error("错误信息: {}, 异常类: {}", e.getMessage(), e.getClass());
        return new Result().fail(ErrorCode.NOT_FOUND);
    }

    /**
     * 请求方法不支持异常
     */
    @ExceptionHandler(HttpRequestMethodNotSupportedException.class)
    public Result httpRequestMethodNotSupportedException(Exception e) {
        log.error("错误信息: {}, 异常类: {}", e.getMessage(), e.getClass());
        return new Result().fail(ErrorCode.METHOD_NOT_ALLOWED);
    }

    /**
     * 请求参数异常
     */
    @ExceptionHandler({HttpMessageNotReadableException.class, MissingServletRequestParameterException.class,
            MissingServletRequestPartException.class, BindException.class})
    public Result parameterException(Exception e) {
        log.error("错误信息: {}, 异常类: {}", e.getMessage(), e.getClass());
        return new Result().fail(ErrorCode.PARAM_ERROR);
    }

    /**
     * 上传文件过大异常
     */
    @ExceptionHandler(MaxUploadSizeExceededException.class)
    public Result maxUploadSizeExceededException(Exception e) {
        log.error("错误信息: {}, 异常类: {}", e.getMessage(), e.getClass());
        return new Result().fail(ErrorCode.FILE_TO_LARGE);
    }

    /**
     * 服务异常
     */
    @ExceptionHandler(ServiceException.class)
    public Result serviceException(ServiceException e) {
        log.error("错误信息: {}, 异常类: {}", e.getMessage(), e.getClass());
        return new Result().fail(e.getData(), e.getMessage(), e.getCode());
    }
}
